#!/usr/bin/env python

#############################################################################
# params.py: Module for parsing the params file.
#############################################################################
# Copyright (C) 2012
#
# Authors: Dan Larkin <danielhlarkin@gmail.com>
#          Yonatan Naamad <cached@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#############################################################################

class Params(object):
    _absent = object
    
    def __init__(self, filepath, schemafile=None):
        #read in parameters
        self.parameters = self.read_parameters(filepath)
        
        #match up to schema
        if schemafile:
            schema = self.read_parameters(schemafile)
            
            #schema format is:     NAME  TYPE  DEFAULT
            for i in schema:
                schema[i] = map(lambda x: x.strip(), schema[i].split(None,1))

            for i in schema:
                try:
                    #figure out which type of object we want
                    f = None
                    f = {'integer': int,
                         'string': str,
                         'float': float,
                         'boolean': lambda x: x.lower() != 'false',
                         'object': lambda x: x
                        }[schema[i][0].lower()]

                    #convert to correct type, or use default value
                    if i in self.parameters:
                        self.parameters[i] = f(self.parameters[i])
                    elif len(schema[i]) > 1:
                        self.parameters[i] = f(schema[i][1])
                        print "Defaulting %s to %s" % (i, self.parameters[i])
                    
                except KeyError:
                    if not f:
                        raise TypeError("'%s' is not a recognized type." % schema[i])
                    else:
                        raise NameError("'%s' not provided in parameters, but appears with no default in schema." % i)
                except ValueError:
                    raise ValueError("'%s is not of type '%s'." % (schema[i][1], schema[i][0].capitalize()))
                
        
    def get(self,name,default=_absent):
        '''Return the value of parameter 'name', resorting to a default
           value if provided.'''
        
        if name in self.parameters:
            return self.parameters[name]
        else:
            if default != Params._absent:
                return default
            else:
                raise NameError("Item '%s' not found in dictionary." % name)
                
    def keys(self):
        '''Return a list of all available parameters'''
        return self.parameters.keys()
    
    def __getitem__(self,name):
        '''Allows for accesses of the form p[PARAM_NAME].'''
        return self.get(name)
        
    def __iter__(self):
        return self.keys().__iter__()

    def read_parameters(self, paramfile):
        '''Read and parse a parameter file.'''
        f = open(paramfile,'r')
        parameters = {}
        
        c = 0
        for line in f:
            c += 1
            l = line.strip()
            
            #delete everything after a hash mark
            if '#' in l:
                l = l[:l.index('#')]
                
            #ignore blank lines (including commented ones)
            if len(l) == 0:
                continue
            else:
                try:
                    a,b = map(lambda x: x.strip(), l.split(None,1))
                    parameters[a] = b
                except e:
                    raise SyntaxError("Parameter file contains syntax error on line %d." % c)
                    
        f.close()
        return parameters
